﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Reflection;
using System.Data.Linq.Mapping;
using System.Linq.Expressions;

using RioTinto.Data.Interfaces;
using RioTinto.Data.Helpers;

namespace RioTinto.Data
{
    /// <summary>
    /// Generic Model Provider 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class DataManager<T> : IDataManager<T> where T : class
    {
        public DataManager()
        {

        }

        /// <summary>
        /// Saves an entity to the database.
        /// </summary>
        /// <param name="entity">Fully populated entity</param>
        /// <returns>Returns a fully populated entity</returns>
        public T Save(T entity)
        {
            ///TODO: In order to support WPF we might need to do a deep copy (clone) of the
            ///entity properties flagged with ColumnAttribute class
            DataContext context = DataContextProvider.GetDataContext();

            if (EntityReflector.GetPrimaryKeyValue(entity) <= default(Int32))
            {
                context.GetTable<T>().InsertOnSubmit(entity);
            }

            context.SubmitChanges();

            return entity;
        }

        /// <summary>
        /// Saves a collection of entities to the database.
        /// </summary>
        /// <param name="list">Fully populated entity collection</param>
        public void SaveAll(IEnumerable<T> list)
        {
            DataContext context = DataContextProvider.GetDataContext();

            foreach (T item in list)
            {
                if (EntityReflector.GetPrimaryKeyValue(item) <= default(Int32))
                {
                    context.GetTable<T>().InsertOnSubmit(item);
                }
            }

            context.SubmitChanges();
        }

        /// <summary>
        /// Deletes an entity from the database.
        /// </summary>
        /// <param name="entity">Fully populated entity</param>
        public void Delete(T entity)
        {
            DataContext context = DataContextProvider.GetDataContext();

            if (entity != null)
                context.GetTable<T>().DeleteOnSubmit(entity);

            context.SubmitChanges();
        }

        /// <summary>
        /// Deletes an entity from the database.
        /// </summary>
        public void Delete(Int32 id)
        {
            DataContext context = DataContextProvider.GetDataContext();

            Expression<Func<T, Boolean>> predicate = EntityReflector.BuildPrimaryKeyPredicate<T>(id);

            T entity = context.GetTable<T>().SingleOrDefault(predicate);

            Delete(entity);
        }

        /// <summary>
        /// Deletes a collection of entities from the database.
        /// </summary>
        public void DeleteAll(IEnumerable<T> entitites)
        {
            DataContext context = DataContextProvider.GetDataContext();

            context.GetTable<T>().DeleteAllOnSubmit(entitites);

            context.SubmitChanges();
        }

        /// <summary>
        /// Gets a single entity from the database.
        /// </summary>
        /// <param name="id">A value for the primary key should be provided.</param>
        /// <returns>Returns a fully populated entity</returns>
        public T GetOne(Int32 id)
        {
            DataContext context = DataContextProvider.GetDataContext();

            Expression<Func<T, Boolean>> predicate = EntityReflector.BuildPrimaryKeyPredicate<T>(id);

            T entity = context.GetTable<T>().SingleOrDefault(predicate);

            return entity;
        }

        public void Refresh(T entity)
        {
            Int32 id = EntityReflector.GetPrimaryKeyValue(entity);

            entity = GetOne(id);
        }

        /// <summary>
        /// Gets a collection of all entities of type T from the database.
        /// </summary>
        public IList<T> GetAll()
        {
            DataContext context = DataContextProvider.GetDataContext();

            return context.GetTable<T>().ToList();
        }

        /// <summary>
        /// Gets a filtered collection of entities from the database.
        /// </summary>
        /// <param name="predicate">A value for predicate should be provided.</param>
        /// <returns>Returns a fully populated collection of entities</returns>
        public IList<T> GetAll(Expression<Func<T, Boolean>> predicate)
        {
            DataContext context = DataContextProvider.GetDataContext();

            return context.GetTable<T>().Where(predicate).ToList();
        }

        /// <summary>
        /// Gets an object which can be queried for entities in the database and applies the predicate as a filter.
        /// </summary>
        /// <param name="predicate">A value for predicate should be provided.</param>
        /// <returns>an unexecuted query with the specified predicate</returns>
        public IQueryable<T> GetQueryable(Expression<Func<T, Boolean>> predicate)
        {
            DataContext context = DataContextProvider.GetDataContext();

            return context.GetTable<T>().Where(predicate);
        }

        /// <summary>
        /// Gets an object which can be queried for entities in the database.
        /// </summary>
        /// <param name="predicate">A value for predicate should be provided.</param>
        /// <returns>a reference to the entity's queryable object</returns>
        public IQueryable<T> GetQueryable()
        {
            DataContext context = DataContextProvider.GetDataContext();

            return context.GetTable<T>();
        }

    }
}
